Skip to content

[Fix]Show joining state after accepting an incoming call#1079

Merged
ipavlidakis merged 1 commit intodevelopfrom
iliaspavlidakis/ios-1475-joining-calling-state-its-never-reachable-while-accepting-a
Mar 12, 2026
Merged

[Fix]Show joining state after accepting an incoming call#1079
ipavlidakis merged 1 commit intodevelopfrom
iliaspavlidakis/ios-1475-joining-calling-state-its-never-reachable-while-accepting-a

Conversation

@ipavlidakis
Copy link
Contributor

@ipavlidakis ipavlidakis commented Mar 11, 2026

🔗 Issue Links

Resolves https://linear.app/stream/issue/IOS-1475/joining-calling-state-its-never-reachable-while-accepting-a-ringing

🎯 Goal

Ensure accepting an incoming call moves CallViewModel into the joining
state immediately so the connecting UI is visible while the async join flow
finishes.

📝 Summary

  • update acceptCall to set callingState to .joining right after
    call.accept()
  • add a regression test that verifies the transient .joining state is
    published before .inCall

🛠 Implementation

acceptCall already accepted the ringing call before delegating to
enterCall, but the UI stayed in the incoming state until the later join
work completed. This change mirrors the existing joinCall behavior by
setting .joining immediately after acceptance succeeds, then continuing
into enterCall.

The new unit test subscribes to subject.$callingState and asserts that
.joining is emitted before the final .inCall state, which protects the
transition that was previously missing.

🎨 Showcase

N/A

🧪 Manual Testing Notes

  1. Receive a ringing call in the demo app.
  2. Tap accept.
  3. Confirm the joining UI appears immediately before the call transitions
    to the in-call UI.

☑️ Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change follows zero ⚠️ policy (required)
  • This change should receive manual QA
  • Changelog is updated with client-facing changes
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (tutorial, CMS)

Summary by CodeRabbit

  • Bug Fixes

    • Improved incoming call experience: the joining UI now appears immediately when accepting an incoming call, reducing visual delay.
  • Tests

    • Added test coverage to verify the incoming-call accept flow transitions through the joining state before entering the active call.

@ipavlidakis ipavlidakis self-assigned this Mar 11, 2026
@ipavlidakis ipavlidakis requested a review from a team as a code owner March 11, 2026 12:55
@ipavlidakis ipavlidakis added the bug Something isn't working label Mar 11, 2026
@coderabbitai
Copy link

coderabbitai bot commented Mar 11, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bcce8d62-2dc4-4918-930d-229586cdef92

📥 Commits

Reviewing files that changed from the base of the PR and between d50af14 and d7196ad.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • Sources/StreamVideoSwiftUI/CallViewModel.swift
  • StreamVideoSwiftUITests/CallViewModel_Tests.swift
🚧 Files skipped from review as they are similar to previous changes (2)
  • StreamVideoSwiftUITests/CallViewModel_Tests.swift
  • Sources/StreamVideoSwiftUI/CallViewModel.swift

📝 Walkthrough

Walkthrough

The incoming call acceptance flow now sets CallViewModel's calling state to .joining on the main actor immediately after call.accept() completes, before entering the call, so the joining UI appears promptly.

Changes

Cohort / File(s) Summary
Core Logic Update
Sources/StreamVideoSwiftUI/CallViewModel.swift
After call.accept() the acceptCall path updates callingState to .joining on the MainActor prior to calling enterCall, aligning timing with joinCall.
Test Coverage
StreamVideoSwiftUITests/CallViewModel_Tests.swift
Added import Combine and a new test test_incomingCall_acceptCall_updatesCallingStateToJoiningBeforeInCall asserting transient .joining state before .inCall.
Documentation
CHANGELOG.md
Added changelog entry documenting the incoming call acceptance flow update.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

🐰 A bell rings soft, I hop in place,
I set to joining—quickening pace,
A blink, then in the call I go,
Smooth transitions, gentle flow.
Hooray for states that show! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: ensuring the joining state is shown after accepting an incoming call, which matches the primary objective of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch iliaspavlidakis/ios-1475-joining-calling-state-its-never-reachable-while-accepting-a
📝 Coding Plan for PR comments
  • Generate coding plan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@StreamVideoSwiftUITests/CallViewModel_Tests.swift`:
- Around line 409-435: The test
test_incomingCall_acceptCall_updatesCallingStateToJoiningBeforeInCall currently
only verifies that .joining is emitted sometime before the final
assertCallingState(.inCall) and can miss ordering regressions; modify the test
to record the sequence of emitted callingState values from subject.$callingState
(use dropFirst() as before), collect them into an array until the final .inCall
is observed (or use an expectation that fulfills when .inCall is seen), then
assert that the observed transition sequence contains the ordered subsequence
[.joining, .inCall] (i.e., the first relevant states are [.joining, .inCall])
after calling subject.acceptCall(callType: callType, callId: callId) and before
calling assertCallingState(.inCall).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f176efca-1502-4c0e-8c7d-f422cdc03e40

📥 Commits

Reviewing files that changed from the base of the PR and between 007e729 and d50af14.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • Sources/StreamVideoSwiftUI/CallViewModel.swift
  • StreamVideoSwiftUITests/CallViewModel_Tests.swift

Comment on lines +409 to +435
func test_incomingCall_acceptCall_updatesCallingStateToJoiningBeforeInCall() async throws {
// Given
await prepareIncomingCallScenario()
let joiningStateExpectation = expectation(
description: "CallingState becomes joining"
)
joiningStateExpectation.assertForOverFulfill = false
var cancellable: AnyCancellable?

// Capture the transient state because `acceptCall` continues into the
// async `enterCall` flow immediately after the acceptance request.
cancellable = subject.$callingState
.dropFirst()
.sink { state in
if state == .joining {
joiningStateExpectation.fulfill()
}
}
defer { cancellable?.cancel() }

// When
subject.acceptCall(callType: callType, callId: callId)

// Then
await fulfillment(of: [joiningStateExpectation], timeout: defaultTimeout)
await assertCallingState(.inCall)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Assert the transition order explicitly.

This only proves that .joining is emitted at some point before the final assertion. A sequence like .inCall → .joining → .inCall would still pass, so the regression on intermediate-state ordering is not fully covered. Record the emitted states and assert the first relevant transition is [.joining, .inCall].

Suggested test tightening
-        let joiningStateExpectation = expectation(
-            description: "CallingState becomes joining"
-        )
-        joiningStateExpectation.assertForOverFulfill = false
+        let stateSequenceExpectation = expectation(
+            description: "CallingState emits joining before inCall"
+        )
         var cancellable: AnyCancellable?
+        var emittedStates: [CallingState] = []

-        // Capture the transient state because `acceptCall` continues into the
-        // async `enterCall` flow immediately after the acceptance request.
         cancellable = subject.$callingState
             .dropFirst()
             .sink { state in
-                if state == .joining {
-                    joiningStateExpectation.fulfill()
+                emittedStates.append(state)
+                if Array(emittedStates.suffix(2)) == [.joining, .inCall] {
+                    stateSequenceExpectation.fulfill()
                 }
             }
         defer { cancellable?.cancel() }
@@
-        await fulfillment(of: [joiningStateExpectation], timeout: defaultTimeout)
+        await fulfillment(of: [stateSequenceExpectation], timeout: defaultTimeout)
+        XCTAssertEqual(Array(emittedStates.prefix(2)), [.joining, .inCall])
         await assertCallingState(.inCall)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@StreamVideoSwiftUITests/CallViewModel_Tests.swift` around lines 409 - 435,
The test test_incomingCall_acceptCall_updatesCallingStateToJoiningBeforeInCall
currently only verifies that .joining is emitted sometime before the final
assertCallingState(.inCall) and can miss ordering regressions; modify the test
to record the sequence of emitted callingState values from subject.$callingState
(use dropFirst() as before), collect them into an array until the final .inCall
is observed (or use an expectation that fulfills when .inCall is seen), then
assert that the observed transition sequence contains the ordered subsequence
[.joining, .inCall] (i.e., the first relevant states are [.joining, .inCall])
after calling subject.acceptCall(callType: callType, callId: callId) and before
calling assertCallingState(.inCall).

Copy link
Contributor

@martinmitrevski martinmitrevski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to check the e2e tests failing

Move CallViewModel into .joining as soon as an incoming call is accepted
so the joining UI is shown while enterCall completes asynchronously.

Add a regression test that verifies the transient joining state is emitted
before the final in-call state, and document the fix in the changelog.

# Conflicts:
#	CHANGELOG.md
@ipavlidakis ipavlidakis force-pushed the iliaspavlidakis/ios-1475-joining-calling-state-its-never-reachable-while-accepting-a branch from d50af14 to d7196ad Compare March 12, 2026 14:04
@github-actions
Copy link

Public Interface

🚀 No changes affecting the public interface.

@Stream-SDK-Bot
Copy link
Collaborator

SDK Size

title develop branch diff status
StreamVideo 10.08 MB 10.08 MB 0 KB 🟢
StreamVideoSwiftUI 2.45 MB 2.45 MB 0 KB 🟢
StreamVideoUIKit 2.58 MB 2.58 MB 0 KB 🟢
StreamWebRTC 11.09 MB 11.09 MB 0 KB 🟢

@Stream-SDK-Bot
Copy link
Collaborator

StreamVideoSwiftUI XCSize

Object Diff (bytes)
CallViewModel.o +1376

@sonarqubecloud
Copy link

@ipavlidakis ipavlidakis merged commit 8f18d49 into develop Mar 12, 2026
13 checks passed
@ipavlidakis ipavlidakis deleted the iliaspavlidakis/ios-1475-joining-calling-state-its-never-reachable-while-accepting-a branch March 12, 2026 15:18
This was referenced Mar 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants